/* transformjs 1.1.6
 * By dntzhang
 * Github: https://github.com/AlloyTeam/PhyTouch/tree/master/transformjs
 */
(function () {
  var DEG_TO_RAD = 0.017453292519943295;

  var Matrix3D = function (
    n11,
    n12,
    n13,
    n14,
    n21,
    n22,
    n23,
    n24,
    n31,
    n32,
    n33,
    n34,
    n41,
    n42,
    n43,
    n44
  ) {
    this.elements = window.Float32Array ? new Float32Array(16) : [];
    var te = this.elements;
    te[0] = n11 !== undefined ? n11 : 1;
    te[4] = n12 || 0;
    te[8] = n13 || 0;
    te[12] = n14 || 0;
    te[1] = n21 || 0;
    te[5] = n22 !== undefined ? n22 : 1;
    te[9] = n23 || 0;
    te[13] = n24 || 0;
    te[2] = n31 || 0;
    te[6] = n32 || 0;
    te[10] = n33 !== undefined ? n33 : 1;
    te[14] = n34 || 0;
    te[3] = n41 || 0;
    te[7] = n42 || 0;
    te[11] = n43 || 0;
    te[15] = n44 !== undefined ? n44 : 1;
  };

  Matrix3D.prototype = {
    set: function (
      n11,
      n12,
      n13,
      n14,
      n21,
      n22,
      n23,
      n24,
      n31,
      n32,
      n33,
      n34,
      n41,
      n42,
      n43,
      n44
    ) {
      var te = this.elements;
      te[0] = n11;
      te[4] = n12;
      te[8] = n13;
      te[12] = n14;
      te[1] = n21;
      te[5] = n22;
      te[9] = n23;
      te[13] = n24;
      te[2] = n31;
      te[6] = n32;
      te[10] = n33;
      te[14] = n34;
      te[3] = n41;
      te[7] = n42;
      te[11] = n43;
      te[15] = n44;
      return this;
    },
    identity: function () {
      this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
      return this;
    },
    multiplyMatrices: function (a, be) {
      var ae = a.elements;
      var te = this.elements;
      var a11 = ae[0],
        a12 = ae[4],
        a13 = ae[8],
        a14 = ae[12];
      var a21 = ae[1],
        a22 = ae[5],
        a23 = ae[9],
        a24 = ae[13];
      var a31 = ae[2],
        a32 = ae[6],
        a33 = ae[10],
        a34 = ae[14];
      var a41 = ae[3],
        a42 = ae[7],
        a43 = ae[11],
        a44 = ae[15];

      var b11 = be[0],
        b12 = be[1],
        b13 = be[2],
        b14 = be[3];
      var b21 = be[4],
        b22 = be[5],
        b23 = be[6],
        b24 = be[7];
      var b31 = be[8],
        b32 = be[9],
        b33 = be[10],
        b34 = be[11];
      var b41 = be[12],
        b42 = be[13],
        b43 = be[14],
        b44 = be[15];

      te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
      te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
      te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
      te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;

      te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
      te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
      te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
      te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;

      te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
      te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
      te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
      te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;

      te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
      te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
      te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
      te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;

      return this;
    },
    // 解决角度为90的整数倍导致Math.cos得到极小的数，其实是0。导致不渲染
    _rounded: function (value, i) {
      i = Math.pow(10, i || 15);
      // default
      return Math.round(value * i) / i;
    },
    _arrayWrap: function (arr) {
      return window.Float32Array ? new Float32Array(arr) : arr;
    },
    appendTransform: function (
      x,
      y,
      z,
      scaleX,
      scaleY,
      scaleZ,
      rotateX,
      rotateY,
      rotateZ,
      skewX,
      skewY,
      originX,
      originY,
      originZ
    ) {
      var rx = rotateX * DEG_TO_RAD;
      var cosx = this._rounded(Math.cos(rx));
      var sinx = this._rounded(Math.sin(rx));
      var ry = rotateY * DEG_TO_RAD;
      var cosy = this._rounded(Math.cos(ry));
      var siny = this._rounded(Math.sin(ry));
      var rz = rotateZ * DEG_TO_RAD;
      var cosz = this._rounded(Math.cos(rz * -1));
      var sinz = this._rounded(Math.sin(rz * -1));

      this.multiplyMatrices(
        this,
        this._arrayWrap([
          1,
          0,
          0,
          x,
          0,
          cosx,
          sinx,
          y,
          0,
          -sinx,
          cosx,
          z,
          0,
          0,
          0,
          1,
        ])
      );

      this.multiplyMatrices(
        this,
        this._arrayWrap([
          cosy,
          0,
          siny,
          0,
          0,
          1,
          0,
          0,
          -siny,
          0,
          cosy,
          0,
          0,
          0,
          0,
          1,
        ])
      );

      this.multiplyMatrices(
        this,
        this._arrayWrap([
          cosz * scaleX,
          sinz * scaleY,
          0,
          0,
          -sinz * scaleX,
          cosz * scaleY,
          0,
          0,
          0,
          0,
          1 * scaleZ,
          0,
          0,
          0,
          0,
          1,
        ])
      );

      if (skewX || skewY) {
        this.multiplyMatrices(
          this,
          this._arrayWrap([
            this._rounded(Math.cos(skewX * DEG_TO_RAD)),
            this._rounded(Math.sin(skewX * DEG_TO_RAD)),
            0,
            0,
            -1 * this._rounded(Math.sin(skewY * DEG_TO_RAD)),
            this._rounded(Math.cos(skewY * DEG_TO_RAD)),
            0,
            0,
            0,
            0,
            1,
            0,
            0,
            0,
            0,
            1,
          ])
        );
      }

      if (originX || originY || originZ) {
        this.elements[12] -=
          originX * this.elements[0] +
          originY * this.elements[4] +
          originZ * this.elements[8];
        this.elements[13] -=
          originX * this.elements[1] +
          originY * this.elements[5] +
          originZ * this.elements[9];
        this.elements[14] -=
          originX * this.elements[2] +
          originY * this.elements[6] +
          originZ * this.elements[10];
      }
      return this;
    },
  };

  var Matrix2D = function (a, b, c, d, tx, ty) {
    this.a = a == null ? 1 : a;
    this.b = b || 0;
    this.c = c || 0;
    this.d = d == null ? 1 : d;
    this.tx = tx || 0;
    this.ty = ty || 0;
    return this;
  };

  Matrix2D.prototype = {
    identity: function () {
      this.a = this.d = 1;
      this.b = this.c = this.tx = this.ty = 0;
      return this;
    },
    appendTransform: function (
      x,
      y,
      scaleX,
      scaleY,
      rotation,
      skewX,
      skewY,
      originX,
      originY
    ) {
      if (rotation % 360) {
        var r = rotation * DEG_TO_RAD;
        var cos = Math.cos(r);
        var sin = Math.sin(r);
      } else {
        cos = 1;
        sin = 0;
      }
      if (skewX || skewY) {
        skewX *= DEG_TO_RAD;
        skewY *= DEG_TO_RAD;
        this.append(
          Math.cos(skewY),
          Math.sin(skewY),
          -Math.sin(skewX),
          Math.cos(skewX),
          x,
          y
        );
        this.append(
          cos * scaleX,
          sin * scaleX,
          -sin * scaleY,
          cos * scaleY,
          0,
          0
        );
      } else {
        this.append(
          cos * scaleX,
          sin * scaleX,
          -sin * scaleY,
          cos * scaleY,
          x,
          y
        );
      }
      if (originX || originY) {
        this.tx -= originX * this.a + originY * this.c;
        this.ty -= originX * this.b + originY * this.d;
      }
      return this;
    },
    append: function (a, b, c, d, tx, ty) {
      var a1 = this.a;
      var b1 = this.b;
      var c1 = this.c;
      var d1 = this.d;
      this.a = a * a1 + b * c1;
      this.b = a * b1 + b * d1;
      this.c = c * a1 + d * c1;
      this.d = c * b1 + d * d1;
      this.tx = tx * a1 + ty * c1 + this.tx;
      this.ty = tx * b1 + ty * d1 + this.ty;
      return this;
    },
    initialize: function (a, b, c, d, tx, ty) {
      this.a = a;
      this.b = b;
      this.c = c;
      this.d = d;
      this.tx = tx;
      this.ty = ty;
      return this;
    },
    setValues: function (a, b, c, d, tx, ty) {
      this.a = a == null ? 1 : a;
      this.b = b || 0;
      this.c = c || 0;
      this.d = d == null ? 1 : d;
      this.tx = tx || 0;
      this.ty = ty || 0;
      return this;
    },
    copy: function (matrix) {
      return this.setValues(
        matrix.a,
        matrix.b,
        matrix.c,
        matrix.d,
        matrix.tx,
        matrix.ty
      );
    },
  };

  function observe(target, props, callback) {
    for (var i = 0, len = props.length; i < len; i++) {
      var prop = props[i];
      watch(target, prop, callback);
    }
  }

  function watch(target, prop, callback) {
    Object.defineProperty(target, prop, {
      get: function () {
        return this["_" + prop];
      },
      set: function (value) {
        this["_" + prop] = value;
        callback();
      },
    });
  }

  function isElement(o) {
    return typeof HTMLElement === "object"
      ? o instanceof HTMLElement //DOM2
      : o &&
          typeof o === "object" &&
          o !== null &&
          o.nodeType === 1 &&
          typeof o.nodeName === "string";
  }

  function Transform(obj, notPerspective) {
    if (obj.___mixCSS3Transform) return;
    var observeProps = [
        "translateX",
        "translateY",
        "translateZ",
        "scaleX",
        "scaleY",
        "scaleZ",
        "rotateX",
        "rotateY",
        "rotateZ",
        "skewX",
        "skewY",
        "originX",
        "originY",
        "originZ",
      ],
      objIsElement = isElement(obj);
    if (!notPerspective) {
      observeProps.push("perspective");
    }
    obj.___mixCSS3Transform = true;
    observe(obj, observeProps, function () {
      var mtx = obj.matrix3d
        .identity()
        .appendTransform(
          obj.translateX,
          obj.translateY,
          obj.translateZ,
          obj.scaleX,
          obj.scaleY,
          obj.scaleZ,
          obj.rotateX,
          obj.rotateY,
          obj.rotateZ,
          obj.skewX,
          obj.skewY,
          obj.originX,
          obj.originY,
          obj.originZ
        );
      var transform =
        (notPerspective ? "" : "perspective(" + obj.perspective + "px) ") +
        "matrix3d(" +
        Array.prototype.slice.call(mtx.elements).join(",") +
        ")";
      if (objIsElement) {
        obj.style.transform =
          obj.style.msTransform =
          obj.style.OTransform =
          obj.style.MozTransform =
          obj.style.webkitTransform =
            transform;
      } else {
        obj.transform = transform;
      }
    });

    obj.matrix3d = new Matrix3D();
    if (!notPerspective) {
      obj.perspective = 500;
    }
    obj.scaleX = obj.scaleY = obj.scaleZ = 1;
    //由于image自带了x\y\z，所有加上translate前缀
    obj.translateX =
      obj.translateY =
      obj.translateZ =
      obj.rotateX =
      obj.rotateY =
      obj.rotateZ =
      obj.skewX =
      obj.skewY =
      obj.originX =
      obj.originY =
      obj.originZ =
        0;
  }

  Transform.getMatrix3D = function (option) {
    var defaultOption = {
      translateX: 0,
      translateY: 0,
      translateZ: 0,
      rotateX: 0,
      rotateY: 0,
      rotateZ: 0,
      skewX: 0,
      skewY: 0,
      originX: 0,
      originY: 0,
      originZ: 0,
      scaleX: 1,
      scaleY: 1,
      scaleZ: 1,
    };
    for (var key in option) {
      if (option.hasOwnProperty(key)) {
        defaultOption[key] = option[key];
      }
    }
    return new Matrix3D()
      .identity()
      .appendTransform(
        defaultOption.translateX,
        defaultOption.translateY,
        defaultOption.translateZ,
        defaultOption.scaleX,
        defaultOption.scaleY,
        defaultOption.scaleZ,
        defaultOption.rotateX,
        defaultOption.rotateY,
        defaultOption.rotateZ,
        defaultOption.skewX,
        defaultOption.skewY,
        defaultOption.originX,
        defaultOption.originY,
        defaultOption.originZ
      ).elements;
  };

  Transform.getMatrix2D = function (option) {
    var defaultOption = {
      translateX: 0,
      translateY: 0,
      rotation: 0,
      skewX: 0,
      skewY: 0,
      originX: 0,
      originY: 0,
      scaleX: 1,
      scaleY: 1,
    };
    for (var key in option) {
      if (option.hasOwnProperty(key)) {
        defaultOption[key] = option[key];
      }
    }
    return new Matrix2D()
      .identity()
      .appendTransform(
        defaultOption.translateX,
        defaultOption.translateY,
        defaultOption.scaleX,
        defaultOption.scaleY,
        defaultOption.rotation,
        defaultOption.skewX,
        defaultOption.skewY,
        defaultOption.originX,
        defaultOption.originY
      );
  };

  if (typeof module !== "undefined" && typeof exports === "object") {
    module.exports = Transform;
  } else {
    window.Transform = Transform;
  }
})();
